using Microsoft.Xna.Framework;
using Rocuna.Graphics.Constants;
using Rocuna.Graphics.Core;

namespace WindowsGameLibrary1.Cameras
{
    /// <summary>
    /// ArcBall camera type.
    /// </summary>
    public class ArcBallCamera : Camera
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ArcBallCamera"/> class.
        /// </summary>
        /// <param name="target">The target.</param>
        /// <param name="rotationX">The rotation X.</param>
        /// <param name="rotationY">The rotation Y.</param>
        /// <param name="minRotationY">The min rotation Y.</param>
        /// <param name="maxRotationY">The max rotation Y.</param>
        /// <param name="distance">The distance.</param>
        /// <param name="minDistance">The min distance.</param>
        /// <param name="maxDistance">The max distance.</param>
        /// <param name="game">The game.</param>
        public ArcBallCamera(Vector3 target, float rotationX, float rotationY, float minRotationY,
            float maxRotationY, float distance, float minDistance, float maxDistance, Game game)
            : base(game)
        {
            Target = target;
            MinRotationY = minRotationY;
            MaxRotationY = maxRotationY;
            RotationY = MathHelper.Clamp(rotationY, MinRotationY, MaxRotationY);
            RotationX = rotationX;
            MinDistance = minDistance;
            MaxDistance = maxDistance;
            Distance = MathHelper.Clamp(distance, MinDistance, MaxDistance);
        }

        #region Properties

        /// <summary>
        /// Gets or sets the rotation X.
        /// </summary>
        /// <value>
        /// The rotation X.
        /// </value>
        public float RotationX { get; set; }

        /// <summary>
        /// Gets or sets the rotation Y.
        /// </summary>
        /// <value>
        /// The rotation Y.
        /// </value>
        public float RotationY { get; set; }

        /// <summary>
        /// Gets or sets the min rotation Y.
        /// </summary>
        /// <value>
        /// The min rotation Y.
        /// </value>
        public float MinRotationY { get; set; }

        /// <summary>
        /// Gets or sets the max rotation Y.
        /// </summary>
        /// <value>
        /// The max rotation Y.
        /// </value>
        public float MaxRotationY { get; set; }

        /// <summary>
        /// Gets or sets the distance.
        /// </summary>
        /// <value>
        /// The distance.
        /// </value>
        public float Distance { get; set; }

        /// <summary>
        /// Gets or sets the min distance.
        /// </summary>
        /// <value>
        /// The min distance.
        /// </value>
        public float MinDistance { get; set; }

        /// <summary>
        /// Gets or sets the max distance.
        /// </summary>
        /// <value>
        /// The max distance.
        /// </value>
        public float MaxDistance { get; set; }

        #endregion

        #region Overrides of Camera

        /// <summary>
        /// Gets the name.
        /// </summary>
        public override string Name
        {
            get { return CamerasNames.ArcBallCamera; }
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Moves the camera the specified distance change.
        /// </summary>
        /// <param name="distanceChange">The distance change.</param>
        public void Move(float distanceChange)
        {
            Distance += distanceChange;
            Distance = MathHelper.Clamp(Distance, MinDistance, MaxDistance);
        }

        /// <summary>
        /// Rotates the camera by the specified rotation values.
        /// </summary>
        /// <param name="rotationXChanged">The rotation X changed.</param>
        /// <param name="rotationYChanged">The rotation Y changed.</param>
        public void Rotate(float rotationXChanged, float rotationYChanged)
        {
            RotationX += rotationXChanged;
            RotationY += rotationYChanged;

            RotationY = MathHelper.Clamp(RotationY, MinRotationY, MaxRotationY);
        }

        /// <summary>
        /// Translates the specified position change.
        /// </summary>
        /// <param name="positionChange">The position change.</param>
        public void Translate(Vector3 positionChange)
        {
            Position += positionChange;
        }

        #endregion

        #region Ovveride methods

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            var rotation = Matrix.CreateFromYawPitchRoll(RotationX, RotationY, 0);

            var translation = new Vector3(0, 0, Distance);

            Position = Target + translation;

            var up = Vector3.Transform(Vector3.Up, rotation);

            View = Matrix.CreateLookAt(Position, Target, up);
        }

        #endregion
    }
}